package com.framework.logaop;

import com.alibaba.fastjson.JSONObject;
import com.framework.thread.ReqComTool;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * Created by pcg on 16/1/21.
 */

/**
 * 日志切面
 */
@Slf4j
@Component
@Aspect
public class LogAspect {

    @Pointcut("execution(* com..*.api..*.*(..))")
    public void controllerAspect() {

    }

    @Before("controllerAspect()")
    public void before() {
    }

    @AfterReturning(pointcut = "controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
    }

    @AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
    public void doAfter(JoinPoint joinPoint, Exception e) {
        handlerLog(joinPoint, e, null);
    }

    @AfterReturning(pointcut = "controllerAspect()", returning = "reData")
    public void afterReturnMethod(JoinPoint joinPoint, Object reData) {
        handlerLog(joinPoint, null, reData);
    }

    private void handlerLog(JoinPoint joinPoint, Exception e, Object reData) {
        try {
            Log log1 = getAnnotion(joinPoint);
            if (log1 == null) {
                return;
            }
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String descInfo = handlerDesc(log1, joinPoint);
            log.info("\n【{}】{} \n reqCom => {} \n resp => {} ",
                    descInfo,
                    request.getRequestURI(),
                    ReqComTool.getReqComDTO(),
                    JSONObject.toJSONString(reData));
        } catch (Exception e1) {
            log.error("保存日志信息失败", e1);
        }
    }



    private static Log getAnnotion(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }

    /**
     * 处理描述信息中的占位符
     *
     * @param log
     * @param joinPoint
     * @return
     */
    private static String handlerDesc(Log log, JoinPoint joinPoint) {
        String desc = log.desc();
        String[] paramsIdx = log.paramsIdx();
        if (paramsIdx == null || paramsIdx.length == 0) {
            return desc;
        }
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < paramsIdx.length; i++) {
            String re = "{" + i + "}";
            String argsVal = getArgsVal(args, paramsIdx[i]);
            if (argsVal == null) {
                argsVal = "";
            }
            desc = desc.replace(re, argsVal);
        }
        return desc;
    }

    /**
     * 从方法参数中获取对应的参数值
     *
     * @param args
     * @param paramsIdx
     * @return
     */
    private static String getArgsVal(Object[] args, String paramsIdx) {
        try {
            if (paramsIdx.indexOf(".") == -1) {
                return String.valueOf(args[Integer.valueOf(paramsIdx)]);
            } else {
                for (Object o : args) {
                    String clsName = o.getClass().getName();
                    clsName = clsName.substring(clsName.lastIndexOf(".") + 1);
                    String[] placeholder = paramsIdx.split("\\.");
                    if (!placeholder[0].equals(clsName)) {
                        continue;
                    }
                    Method m = o.getClass().getDeclaredMethod("get" + captureName(placeholder[1]));
                    Object invokeReturn = m.invoke(o);
                    return invokeReturn == null ? null : invokeReturn.toString();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 首字母大写
     *
     * @param name
     * @return
     */
    public static String captureName(String name) {
        char[] cs = name.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);

    }

}
